import datetime
import json
import platform
import subprocess
import netifaces
import shlex
import urllib.request
from copy import deepcopy
from threading import Thread
from time import sleep

import os

import psutil
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.decorators import action
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, UpdateModelMixin, CreateModelMixin
from rest_framework.response import Response

from oplatform.filters.dvb_config import DvbConfigFilterSet
from oplatform.models import ConfigurationData, Menu, ConfigurationChannel, MachineCodeData, ChannelData
from oplatform.serializers.configuration import ConfigurationSerializer, MenuTreeSerializer, MenuSerializer,\
    MachineCodeSerializer
from oplatform.views import running_pids
from utils.encryption import cal_sha1_string, cal_md5_string, str_to_hex
from utils.rest.decorator import action_name
from utils.rest.viewsets import GenericViewSet
from utils.write_channel import write_config_file
code = 0


class ConfigurationViewSet(ListModelMixin,
                           GenericViewSet,
                           UpdateModelMixin,
                           RetrieveModelMixin,
                           ):
    queryset = ConfigurationData.objects.all()
    serializer_class = ConfigurationSerializer
    filterset_class = DvbConfigFilterSet
    filter_backends = (DjangoFilterBackend, )

    @action_name('修改配置')
    @action(methods=['post'], detail=True)
    def updata_dvb_config(self, request, *args, **kwargs):
        obj = self.get_object()
        data = request.data
        obj.status = data.get('status')
        obj.delivery_system = data.get('delivery_system')
        obj.freq = data.get('freq')
        obj.srate = data.get('srate')
        obj.coderate = data.get('coderate')
        obj.tuner = data.get('tuner')
        obj.modulation = data.get('modulation')
        obj.multicast_iface4 = data.get('multicast_iface4')
        obj.multicast_ipv4 = data.get('multicast_ipv4')
        obj.common_port = data.get('common_port')
        obj.multicast_ttl = data.get('multicast_ttl')
        obj.autoconfiguration = data.get('autoconfiguration')
        obj.multicast_auto_join = data.get('multicast_auto_join')
        obj.ip_http = data.get('ip_http')
        obj.dvr_buffer_size = data.get('dvr_buffer_size')
        obj.dvr_thread = data.get('dvr_thread')
        obj.dvr_thread_buffer_size = data.get('dvr_thread_buffer_size')
        obj.scam_support = data.get('scam_support')
        obj.ring_buffer_default_size = data.get('ring_buffer_default_size')
        obj.decsa_default_delay = data.get('decsa_default_delay')
        obj.send_default_delay = data.get('send_default_delay')
        obj.unicast = data.get('unicast')
        obj.port_http = data.get('port_http')
        obj.psi_tables_filtering = data.get('psi_tables_filtering')
        autoconf_sid_list = data.get('autoconf_sid_list')
        channels = data.get('channels')
        autoconf_sid_list_save_value = ''
        for autoconf_sid_list_item in autoconf_sid_list:
            autoconf_sid_list_save_value += str(autoconf_sid_list_item) + ' '
        obj.autoconf_sid_list = autoconf_sid_list_save_value
        ConfigurationChannel.objects.filter(configurationdata__id=obj.id).delete()
        obj.channels.set([])
        for channel in channels:
            channel_obj = ConfigurationChannel.objects.create(**channel)
            obj.channels.add(channel_obj)
        obj.save()
        write_config_file(obj)
        return Response({'msg': 'success'})

    @action_name('获取设备基础信息')
    @action(methods=['get'], detail=False)
    def get_device_base_info(self, request):
        # 开机时间
        start_time = datetime.datetime.fromtimestamp(psutil.boot_time()).strftime("%Y-%m-%d %H:%M:%S")
        # 总内存 GB
        mem = psutil.virtual_memory()
        memory_total = round(mem.total / 1024 / 1024 / 1024, 1)
        # CPU 信息
        cpu_infos = []
        with open('/proc/cpuinfo') as f:
            for line in f:
                if line.strip():
                    if line.rstrip('\n').startswith('model name'):
                        model_name = line.rstrip('\n').split(':')[1]
                        cpu_infos.append(model_name)
        cpu_info = cpu_infos[0] + ' x ' + str(len(cpu_infos))
        # 硬盘信息 GB
        disk_span = psutil.disk_partitions()
        disk_total = 0
        for disk_item in disk_span:
            disk_total += psutil.disk_usage(disk_item.mountpoint).total
        disk_total_g = round(disk_total / 1024 / 1024 / 1024, 1)
        # 操作系统名称及其版本号
        system_info = platform.platform()
        # 系统版本号
        system_version = platform.version()
        # 操作系统位数
        system_number = platform.architecture()[0]
        result = {
            'cpu_info': cpu_info,
            'memory_total': memory_total,
            'disk_total': disk_total_g,
            'system_info': system_info,
            'system_version': system_version,
            'system_number': system_number,
            'start_time': start_time
        }
        return Response(result)

    @action_name('获取网络信息')
    @action(methods=['get'], detail=False)
    def get_network_info(self, request):
        network_interfaces = netifaces.interfaces()
        net_act = subprocess.getstatusoutput('/etc/init.d/network status')[1].split('\n')[3]
        result = []
        status = ''
        for interface in network_interfaces:
            if interface in net_act:
                status = '启用'
            else:
                status = '禁用'
            if interface != 'lo':
                if netifaces.ifaddresses(interface).get(2) is None:
                    result.append({'name': interface, 'info': None, 'status': status})
                else:
                    is_dhcp = True
                    dns = ''
                    with open('/etc/sysconfig/network-scripts/ifcfg-' + interface, 'r') as f:
                        lines = f.readlines()
                        for line in lines:
                            if 'DNS1' in line:
                                dns = line.split('=')[1]
                    if dns == '':
                        dnss = subprocess.getoutput('cat /etc/resolv.conf').split('\n')
                        for dns_item in dnss:
                            if 'nameserver' in dns_item:
                                dns = dns_item.split(' ')[1]
                                break
                    gateway_info = ''
                    with open('/etc/sysconfig/network-scripts/ifcfg-' + interface, 'r') as f:
                        lines = f.readlines()
                        for line in lines:
                            if 'GATEWAY' in line:
                                gateway_info = line.split('=')[1]
                    # if gateway_info == '':
                    #     gateway_info = netifaces.gateways()[netifaces.AF_INET][0][0]
                    network_info = netifaces.ifaddresses(interface).get(2)[0]
                    network_info['gateway'] = gateway_info
                    network_info['dns'] = dns
                    result.append({'name': interface, 'info': network_info, 'status': status})
        return Response(result)

    @action_name('获取当前运行通道信息')
    @action(methods=['get'], detail=False)
    def get_running_channels(self, request):
        return Response(running_pids)

    @action_name('获取机器码')
    @action(methods=['get'], detail=False)
    def get_machine_code(self, request):
        args_machine_code = "dmesg | grep TBS | awk '/address/{print $8}'"
        code, result = subprocess.getstatusoutput(args_machine_code)
        if code != 0:
            return Response({'msg': '获取失败!', 'result': result}, status=400)
        p = subprocess.Popen(args_machine_code, shell=True, stdout=subprocess.PIPE)
        out = p.stdout.readlines()
        result = ''
        for line in out:
            result += str(line, encoding='utf-8')
        return Response(str_to_hex(result))

    @action_name('注册机器')
    @action(methods=['post'], detail=False)
    def register(self, request):
        register_code = request.data.get('code')
        args_machine_code = "dmesg | grep TBS | awk '/address/{print $8}'"
        code, result = subprocess.getstatusoutput(args_machine_code)
        if code != 0:
            return Response({'msg': '获取失败!', 'result': result}, status=400)
        p = subprocess.Popen(args_machine_code, shell=True, stdout=subprocess.PIPE)
        out = p.stdout.readlines()
        machine_code_result = ''
        for line in out:
            machine_code_result += str(line, encoding='utf-8')
        machine_code_hex = str_to_hex(machine_code_result)
        machine_code_hex_auth = machine_code_hex.replace(' ', '#&#')
        ac_register_code = cal_sha1_string(cal_md5_string(machine_code_hex_auth))
        if register_code == ac_register_code:
            if register_code not in MachineCodeData.objects.all().values_list('code', flat=True):
                machine_code = MachineCodeData.objects.create(**{'code': register_code})
                machine_code.save()
                return Response({'msg': '注册成功'})
        else:
            return Response({'msg': '注册失败, 注册码错误'}, status=400)

    def machine_code_auth(self):
        can_run = False
        args = "dmesg | grep TBS | awk '/address/{print $8}'"
        p = subprocess.Popen(args, shell=True, stdout=subprocess.PIPE)
        out = p.stdout.readlines()
        result = ''
        for line in out:
            result += str(line, encoding='utf-8')
        machine_code_hex = str_to_hex(result)
        machine_code_hex_auth = machine_code_hex.replace(' ', '#&#')
        ac_register_code = cal_sha1_string(cal_md5_string(machine_code_hex_auth))
        codes = MachineCodeData.objects.all()
        for code in codes:
            if ac_register_code == code.code:
                can_run = True
        return can_run

    @action_name('运行通道')
    @action(methods=['post'], detail=False)
    def run_and_stop_channel_status(self, request):
        global code
        if not self.machine_code_auth():
            return Response({'msg': '你的机器未注册'}, status=400)
        op = request.data.get('op')
        channel_id = request.data.get('channel_id')
        config = ConfigurationData.objects.get(id=channel_id)
        config_id = config.id
        if op == 1:
            write_config_file(config)
            args_run_channel = '/opt/media -c ' + '/var/www/channels/channel' + str(config_id) + '.conf -d --card ' + str(config_id - 1)
            if not os.path.exists('/var/www/log/'):
                subprocess.Popen('mkdir /var/www/log', shell=True, stdout=subprocess.PIPE)
            sleep(0.5)
            out_file_path = '/var/www/log/channel' + str(config_id) + '.out'
            error_file_path = '/var/www/log/channel' + str(config_id) + '.error'
            out_file = open(out_file_path, 'w')
            error_file = open(error_file_path, 'w')
            process = subprocess.Popen(args_run_channel, shell=True, stderr=error_file)
            if os.path.exists(error_file_path):
                subprocess.Popen('head -n80 ' + error_file_path, shell=True, stdout=out_file)
            sleep(2)
            if os.path.exists(error_file_path):
                os.remove(error_file_path)
            run_pid = process.pid
            config_ids = []
            for pid in running_pids:
                config_ids.append(pid['channel_id'])
            if config_id not in config_ids:
                config.status = 2
                config.save()
                running_pids.append({'channel_id': config_id, 'pid': run_pid})
            return Response({'msg': 'success', 'pid': run_pid, 'id': config_id})
        else:
            kill_pid = request.data.get('pid')
            args_kill_channel = 'kill -9 ' + str(kill_pid)
            subprocess.Popen(args_kill_channel, shell=True, stdout=subprocess.PIPE)
            for pid in running_pids:
                if pid['pid'] == int(kill_pid):
                    config.status = 1
                    config.save()
                    running_pids.remove(pid)
            return Response({'msg': 'success'})

    @action_name('获取通道运行信息')
    @action(methods=['post'], detail=False)
    def get_running_channel_info(self, request):
        object_id = request.data.get('id')
        response = urllib.request.urlopen('http://127.0.0.1:' + str(7999 + object_id) + '/monitor/channels_traffic.json')
        info = json.loads(str(response.read(), encoding='utf-8'))
        out_file_path = '/var/www/log/channel' + str(object_id) + '.out'
        with open(out_file_path, 'r') as f:
            out = f.readlines()[0:50]
            return Response({'info': info, 'log': out})

    @action_name('修改网卡状态')
    @action(methods=['post'], detail=False)
    def change_network_status(self, request):
        interface = request.data.get('interface')
        status = request.data.get('status')
        op = ''
        if status == 1:
            op = ' up'
        elif status == 2:
            op = ' down'
        args = 'ifconfig ' + interface + op
        code, result = subprocess.getstatusoutput(args)
        if code != 0:
            return Response({'msg': '状态错误!', 'result': result}, status=400)
        subprocess.Popen(args, shell=True, stdout=subprocess.PIPE)
        return Response({'msg': 'success'})

    @action_name('修改网络信息')
    @action(methods=['post'], detail=False)
    def change_network_info(self, request):
        """修改网络信息"""
        data = request.data
        connection_type = data.get('connection_type')
        interface_name = data.get('interface')
        if connection_type == 1:
            # 修改为dhcp
            file_data = ""
            with open('/home/legend/ifcfg-' + interface_name, "r", encoding="utf-8") as f:
                for line in f:
                    if 'BOOTPROTO' in line:
                        line = line.replace(line, 'BOOTPROTO=dhcp\n')
                    file_data += line
            with open('/etc/sysconfig/network-scripts/ifcfg-' + interface_name, "w", encoding="utf-8") as f:
                f.write(file_data)
            # 重启网卡
        else:
            ip = data.get('data').get('addr')
            mask = data.get('data').get('netmask')
            gateway = data.get('data').get('gateway')
            dns = data.get('data').get('dns')
            self.change_network(interface_name, ip, mask, gateway, dns)
        reboot_args = 'service network restart'
        subprocess.Popen(reboot_args, shell=True)
        sleep(4)
        return Response({'msg': 'success'})

    def change_network(self, interface_name, ip, mask, gateway, dns):
        """修改网卡信息"""
        is_exit = False
        with open('/etc/sysconfig/network-scripts/ifcfg-' + interface_name, 'r') as f:
            lines = f.readlines()
            for line in lines:
                if 'IPADDR' in line:
                    is_exit = True
        if not is_exit:
            file_data = ""
            with open('/etc/sysconfig/network-scripts/ifcfg-' + interface_name, 'r') as f:
                lines = f.readlines()
                for line in lines:
                    if 'BOOTPROTO' in line:
                        line = line.replace(line, 'BOOTPROTO=static\n')
                    if 'ONBOOT' in line:
                        line = line.replace(line, 'ONBOOT=yes\n')
                    file_data += line
            file_data += 'IPADDR=' + ip + '\n'
            file_data += 'NETMASK=' + mask + '\n'
            file_data += 'GATEWAY=' + gateway + '\n'
            file_data += 'DNS1=' + dns + '\n'
            with open('/etc/sysconfig/network-scripts/ifcfg-' + interface_name, 'w') as f:
                f.write(file_data)
        else:
            file_data = ""
            with open('/etc/sysconfig/network-scripts/ifcfg-' + interface_name, 'r') as f:
                lines = f.readlines()
                for line in lines:
                    if 'IPADDR' in line:
                        line = line.replace(line, 'IPADDR='+ip + '\n')
                    elif 'NETMASK' in line:
                        line = line.replace(line, 'NETMASK='+mask + '\n')
                    elif 'GATEWAY' in line:
                        line = line.replace(line, 'GATEWAY='+gateway + '\n')
                    elif 'DNS1' in line:
                        line = line.replace(line, 'DNS1='+dns + '\n')
                    elif 'BOOTPROTO' in line:
                        line = line.replace(line, 'BOOTPROTO=static\n')
                    elif 'ONBOOT' in line:
                        line = line.replace(line, 'ONBOOT=yes\n')
                    file_data += line
            with open('/etc/sysconfig/network-scripts/ifcfg-' + interface_name, 'w') as f:
                f.write(file_data)

    @action_name('关机,重启操作')
    @action(methods=['post'], detail=False)
    def exec_shell_command(self, request):
        """用于执行关机,重启"""
        shell_command = request.data['shell']
        command = ''
        if shell_command == 0:
            command = 'init 0'
        elif shell_command == 1:
            command = 'reboot'
        args = shlex.split(command)
        subprocess.Popen(args)
        return Response({'msg': 'success'})

    @action_name('获取网卡名称')
    @action(methods=['get'], detail=False)
    def get_network_name(self, request):
        network_interfaces = netifaces.interfaces()
        interfaces = deepcopy(network_interfaces)
        for interface in interfaces:
            if interface == 'lo':
                network_interfaces.remove(interface)
        return Response(network_interfaces)

    @action_name('扫描节目进程是否存在')
    @action(methods=['get'], detail=False)
    def is_scan_channel(self, request):
        pids = psutil.pids()
        is_exit = False
        for pid in pids:
            p = psutil.Process(pid)
            if 'w_scan' in p.name():
                is_exit = True
        return Response({'msg': is_exit})

    @action_name('导出节目文件')
    @action(methods=['get'], detail=False)
    def download_channel_file(self, request):
        result = ''
        with open('/var/www/channels.txt', 'r', encoding='utf-8', errors='ignore') as f:
            lines = f.readlines()
            for line in lines:
                result += line
        return Response(result)

    @action_name('上传节目文件')
    @action(methods=['post'], detail=False)
    def post_channel_file(self, request):
        all_channels = []
        file = request.FILES.get('file')
        ChannelData.objects.all().delete()
        id = 1
        lines = file.readlines()
        for line in lines:
            item_info = str(line, 'utf-8', errors='ignore')
            if ':' in item_info:
                channel_split = item_info.split(':')
                name = channel_split[0]
                provider_name = channel_split[1]
                coderate = channel_split[2]
                freq = channel_split[3]
                modulation = channel_split[4]
                delivery_system = channel_split[5]
                srate = channel_split[6]
                sid = channel_split[7]
                pmt = channel_split[8]
                pcr = channel_split[9]
                video_id = channel_split[10]
                audio_id = channel_split[11]
                subtitle = channel_split[12]
                caid = channel_split[13]
                channel_item = {
                    'id': id,
                    'name': name,
                    'provider_name': provider_name,
                    'coderate': coderate,
                    'freq': freq,
                    'modulation': modulation,
                    'delivery_system': delivery_system,
                    'srate': srate,
                    'sid': sid,
                    'pmt': pmt,
                    'pcr': pcr,
                    'video_id': video_id,
                    'audio_id': audio_id,
                    'subtitle': subtitle,
                    'caid': caid,
                }
                all_channels.append(channel_item)
                id += 1
        for channel in all_channels:
            ChannelData.objects.get_or_create(
                id=channel.get('id'),
                name=channel.get('name'),
                provider_name=channel.get('provider_name'),
                coderate=channel.get('coderate'),
                freq=channel.get('freq'),
                modulation=channel.get('modulation'),
                delivery_system=channel.get('delivery_system'),
                srate=channel.get('srate'),
                sid=channel.get('sid'),
                pmt=channel.get('pmt'),
                pcr=channel.get('pcr'),
                video_id=channel.get('video_id'),
                audio_id=channel.get('audio_id'),
                subtitle=channel.get('subtitle'),
                caid=channel.get('caid'),
            )
        return Response({'msg': '导入成功!'})

    @action_name('获取通道运行时间')
    @action(methods=['get'], detail=False)
    def get_channels_running_time(self, request):
        result = []
        infos = subprocess.getoutput('ps -eo etime,cmd | grep media').split('\n')
        for info in infos:
            if 'card' in info:
                lsinfo = info.lstrip().split(' ')
                result.append({'time': lsinfo[0], 'id': int(lsinfo[-1]) + 1})
        return Response(result)


class MenuViewSet(GenericViewSet):
    queryset = Menu.objects.all()
    serializer_class = MenuTreeSerializer

    @action_name('获取全部菜单树')
    @action(methods=['GET'], detail=False)
    def tree(self, request):
        self.queryset = self.queryset.filter(father=None)
        serializer = self.serializer_class(self.queryset, many=True)
        list_serializer = MenuSerializer(self.queryset, many=True)
        return Response({'list': list_serializer.data, 'tree': serializer.data})


class MachineCodeViewSet(GenericViewSet, CreateModelMixin):
    queryset = MachineCodeData.objects.all()
    serializer_class = MachineCodeSerializer
